package com.falko.android.raven.renderer;

import java.io.File;
import java.util.ArrayList;

import javax.microedition.khronos.egl.EGLConfig;
import javax.microedition.khronos.opengles.GL10;

import android.content.Context;

import com.falko.android.raven.animator.Animation;
import com.falko.android.raven.animator.AnimationQueue;

public class AnimationRenderer implements GLSurfaceView.Renderer {

	@SuppressWarnings("unused")
	private final String TAG = getClass().getName();
	private static final int DEFAULT_ANIMATION_COUNT = 5;
	private static final int DEFAULT_QUEUE_SIZE = 5;

	private TextureLibrary textureLibrary_ = null;
	private Context context_ = null;
	private BackgroundImage background_ = null;
	private AnimationQueue animationQueue_ = null;
	private AnimationQueue recycleQueue_ = null;
	private int maxAnimations_ = DEFAULT_ANIMATION_COUNT;
	private ArrayList<Animation> runningAnimations_ = null;
	private long preUpdateTime_ = 0;

	// user prefs
	enum BackgroundMode {
		IMAGE, COLOR, ERROR;

		public static BackgroundMode fromString(String val) {
			return IMAGE.toString().equalsIgnoreCase(val) ? IMAGE : COLOR
					.toString().equalsIgnoreCase(val) ? COLOR : ERROR;
		}
	}

	private float color_[];
	private BackgroundMode backgroundMode_;

	public AnimationRenderer(Context context, TextureLibrary texLib,
			AnimationQueue recycleQueue) {
		textureLibrary_ = texLib;
		context_ = context;
		animationQueue_ = new AnimationQueue(DEFAULT_QUEUE_SIZE);
		recycleQueue_ = recycleQueue;
		color_ = new float[4];
		background_ = new BackgroundImage();
		runningAnimations_ = new ArrayList<Animation>();
	}

	public void onSurfaceCreated(GL10 gl, EGLConfig config) {
		// following code is from SimpleGLRenderer
		// in package com.android.spritemethodtest
		// http://code.google.com/p/apps-for-android/

		/*
		 * Some one-time OpenGL initialization can be made here probably based
		 * on features of this particular context
		 */
		gl.glHint(GL10.GL_PERSPECTIVE_CORRECTION_HINT, GL10.GL_FASTEST);

		gl.glClearColor(color_[1], color_[2], color_[3], 1.0f);
		gl.glShadeModel(GL10.GL_FLAT);
		gl.glDisable(GL10.GL_DEPTH_TEST);
		gl.glEnable(GL10.GL_TEXTURE_2D);
		/*
		 * By default, OpenGL enables features that improve quality but reduce
		 * performance. One might want to tweak that especially on software
		 * renderer.
		 */
		gl.glDisable(GL10.GL_DITHER);
		gl.glDisable(GL10.GL_LIGHTING);

		gl.glClear(GL10.GL_COLOR_BUFFER_BIT | GL10.GL_DEPTH_BUFFER_BIT);

		if (textureLibrary_ != null) {
			textureLibrary_.invalidateAll();
			textureLibrary_.loadAll(context_, gl);
		}

		if (background_.path_ != null) {
			background_.invalidate();
		}
	}

	public void onSurfaceChanged(GL10 gl, int width, int height) {
		// following code is from SimpleGLRenderer
		// in package com.android.spritemethodtest
		// http://code.google.com/p/apps-for-android/

		gl.glViewport(0, 0, width, height);
		// gl.glViewport(0, height, width, 0);

		/*
		 * Set our projection matrix. This doesn't have to be done each time we
		 * draw, but usually a new projection needs to be set when the viewport
		 * is resized.
		 */

		gl.glLoadIdentity();
		gl.glMatrixMode(GL10.GL_PROJECTION);

		gl.glOrthof(0.0f, width, 0.0f, height, 0.0f, 0.0f);
		gl.glShadeModel(GL10.GL_FLAT);
		gl.glEnable(GL10.GL_BLEND);
		gl.glBlendFunc(GL10.GL_SRC_ALPHA, GL10.GL_ONE_MINUS_SRC_ALPHA);
		gl.glEnable(GL10.GL_TEXTURE_2D);

		background_.width_ = width;
		background_.height_ = height;
		
		if (background_.path_ != null && !background_.texture_.loaded && backgroundMode_ == BackgroundMode.IMAGE) {
			background_.load(gl);
		}

		if (backgroundMode_ == BackgroundMode.IMAGE
				&& background_.texture_.loaded) {
			background_.draw(gl);
		} else {
			gl.glColor4x(0x10000, 0x10000, 0x10000, 0x10000);
			gl.glClearColor(color_[1], color_[2], color_[3], 1.0f);
		}
	}

	public void onDrawFrame(GL10 gl) {

		gl.glMatrixMode(GL10.GL_MODELVIEW);
		gl.glEnable(GL10.GL_TEXTURE_2D);

		if (backgroundMode_ == BackgroundMode.IMAGE
				&& background_.texture_.loaded) {
			background_.draw(gl);
		} else {
			gl.glClear(GL10.GL_COLOR_BUFFER_BIT | GL10.GL_DEPTH_BUFFER_BIT);
		}

		if (runningAnimations_ == null) {
			return;
		}

		for (int i = 0; i < runningAnimations_.size(); ++i) {
			Animation a = runningAnimations_.get(i);
			a.render(gl);
		}

		long totalUpdateRenderTime = System.currentTimeMillis()
				- preUpdateTime_;
		if (totalUpdateRenderTime < 16) {
			try {
				Thread.sleep(16 - totalUpdateRenderTime);
			} catch (InterruptedException e) {
				// ignore
			}
		}

	}

	public void update() {

		preUpdateTime_ = System.currentTimeMillis();

		if (runningAnimations_.size() < maxAnimations_
				&& animationQueue_.size() > 0) {
			runningAnimations_.add(animationQueue_.take());
		}

		if (!runningAnimations_.isEmpty()) {
			// Log.v(TAG, "Start Update");

			for (int i = 0; i < runningAnimations_.size(); ++i) {
				Animation a = runningAnimations_.get(i);
				a.update();

				if (!a.visible()) {
					runningAnimations_.remove(i);
					recycleQueue_.putAnimation(a);
				}
			}
		}

	}

	public boolean needDraw() {
		return !runningAnimations_.isEmpty();
	}

	public boolean addAnimation(Animation a) {

		if (animationQueue_.isFull()) {
			return false;
		}

		animationQueue_.putAnimation(a);
		return true;
	}

	public void setBackgroundColor(long color) {

		// alpha 0, red 1, green 2, blue 3
		color_[0] = ((color >>> 24) & 0xFF) / 225f;
		color_[1] = ((color >>> 16) & 0xFF) / 225f;
		color_[2] = ((color >>> 8) & 0xFF) / 225f;
		color_[3] = (color & 0xFF) / 225f;
	}

	public void setBackgroundImage(String path) {
		if (path == null || new File(path).exists())
			background_.path_ = path;
		else
			background_.path_ = null;
	}

	public void setBackgroundMode(String mode) {
		backgroundMode_ = BackgroundMode.fromString(mode);
	}

	public void setBackgroundScale(float scaleX, float scaleY) {
		background_.scaleX_ = scaleX;
		background_.scaleY_ = scaleY;
	}

}
